﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using UtilZ.Dotnet.Ex.Base;
using UtilZ.Dotnet.Ex.Cache;
using UtilZ.Dotnet.Ex.Exceptions;
using UtilZ.Dotnet.Ex.Model;
using UtilZ.Dotnet.Ex.ZLog;

namespace UtilZ.Dotnet.Ex.Communication.Net.Web
{
    /// <summary>
    /// 会话管理中间件
    /// </summary>
    public class SessionManagerMiddleware : MiddlewareAbs
    {
        /// <summary>
        /// 会话有效时长，单位：毫秒.默认20分钟
        /// </summary>
        private long _sessionExpireMilliseconds = 1000 * 60 * 20;
        /// <summary>
        /// 会话有效时长，单位：毫秒.默认20分钟
        /// </summary>
        public long SessionExpireMilliseconds
        {
            get { return _sessionExpireMilliseconds; }
            set { _sessionExpireMilliseconds = value; }
        }


        /// <summary>
        /// key:会话标识;value:会话数据
        /// </summary>
        private readonly ObjectCache _sessionCache = new ObjectCache();

        /// <summary>
        /// 获取传话标识回调[参数1:HttpRequest;返回值:会话标识]
        /// </summary>
        private readonly Func<HttpRequest, object> _getSessionIdCallback;

        /// <summary>
        /// 会话过期通知回调[参数1:会话标识;参数2:会话数据]
        /// </summary>
        private readonly Action<object, IHttpSession> _sessionExpireCallback;


        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="getSessionIdCallback">获取传话标识回调[参数1:HttpRequest;返回值:会话标识],如果为null则默认使用cookie</param>
        /// <param name="sessionExpireCallback">会话过期通知回调[参数1:会话标识;参数2:会话],为null则忽略</param>
        public SessionManagerMiddleware(Func<HttpRequest, object> getSessionIdCallback = null, Action<object, IHttpSession> sessionExpireCallback = null)
        {
            this._getSessionIdCallback = getSessionIdCallback;
            this._sessionExpireCallback = sessionExpireCallback;
        }




        /// <summary>
        /// 尝试调用中间件处理[已处理返回true,否则返回false]
        /// </summary>
        /// <param name="request">http请求</param>
        /// <param name="response">http响应</param>
        /// <returns>已处理返回true,否则返回false</returns>
        protected override bool PrimitiveTryInvoke(HttpRequest request, out IHttpResponse response)
        {
            response = null;
            object sessionId;
            if (this._getSessionIdCallback == null)
            {
                string cookie;
                if (request.Headers.TryGetValue(HttpRequestHeaders.Cookie, out cookie))
                {
                    sessionId = cookie;
                }
                else
                {
                    return this.SessionNotExist(request, out response);
                }
            }
            else
            {
                sessionId = this._getSessionIdCallback(request);
            }

            request.Session = this.GetSession(sessionId);
            if (request.Session == null)
            {
                //会话管理中间件只管理找会话,并不是处理完成,所以管道继续传递下去处理.
                //如果已经处理完成,则不再传递下去
                return this.SessionExire(sessionId, request, out response);
            }

            //传话数据关系成功，返回false
            return false;
        }

        /// <summary>
        /// 会话已过期或在会话管理器中找不着会话
        /// </summary>
        /// <param name="sessionId">会话标识</param>
        /// <param name="request">http请求</param>
        /// <param name="response">http响应</param>
        /// <returns>已处理返回true,否则返回false</returns>
        protected virtual bool SessionExire(object sessionId, HttpRequest request, out IHttpResponse response)
        {
            response = new HttpResponse(request.Connection);
            response.StatusCode = HttpStatusCodes.Unauthorized;
            return false;
        }

        /// <summary>
        /// http中没有cookie
        /// </summary>
        /// <param name="request">http请求</param>
        /// <param name="response">http响应</param>
        /// <returns>已处理返回true,否则返回false</returns>
        protected virtual bool SessionNotExist(HttpRequest request, out IHttpResponse response)
        {
            response = new HttpResponse(request.Connection);
            response.StatusCode = HttpStatusCodes.Unauthorized;
            return false;
        }


        private void SessionRemovedCallback(CacheEntryRemovedArguments cacheEntry)
        {
            try
            {
                using (var session = (IHttpSession)cacheEntry.CacheItem.Value)
                {
                    this._sessionExpireCallback?.Invoke(cacheEntry.Key, session);
                }
            }
            catch (Exception ex)
            {
                ZLoger.Error(ex);
            }
        }


        /// <summary>
        /// 设置会话数据,使用默认有效时长
        /// </summary>
        /// <param name="sessionId">会话标识</param>
        /// <param name="session">会话数据</param>
        public void SetCookie(object sessionId, IHttpSession session)
        {
            if (base._disposed)
            {
               throw new ObjectDisposedException(this.ToString());
            }

            this._sessionCache.SetSlide(sessionId, session, this._sessionExpireMilliseconds, this.SessionRemovedCallback);
        }

        /// <summary>
        /// 设置会话
        /// </summary>
        /// <param name="sessionId">会话标识</param>
        /// <param name="session">会话数据</param>
        /// <param name="sessionExpireMilliseconds">会话有效时长</param>
        public void SetSession(object sessionId, IHttpSession session, long sessionExpireMilliseconds)
        {
            if (base._disposed)
            {
                throw new ObjectDisposedException(this.ToString());
            }

            this._sessionCache.SetSlide(sessionId, session, sessionExpireMilliseconds, this.SessionRemovedCallback);
        }

        /// <summary>
        /// 移除会话
        /// </summary>
        /// <param name="sessionId">会话标识</param>
        public void RemoveSession(object sessionId)
        {
            this._sessionCache.Remove(sessionId);
        }

        /// <summary>
        /// 获取会话
        /// </summary>
        /// <param name="sessionId">会话标识</param>
        /// <returns>会话数据</returns>
        public IHttpSession GetSession(object sessionId)
        {
            return (IHttpSession)this._sessionCache.Get(sessionId);
        }


        /// <summary>
        /// Dispose
        /// </summary>
        /// <param name="disposing">disposing</param>
        protected override void Dispose(bool disposing)
        {
            ICollection<CacheItem> values = this._sessionCache.Values;
            this._sessionCache.Dispose();
            IHttpSession session;
            foreach (var item in values)
            {
                session = item.Value as IHttpSession;
                if (session != null)
                {
                    session.Dispose();
                }
            }
            base.Dispose(disposing);
        }
    }
}
